//
// Copyright 2001-2004 Stottler Henke Associates, Inc.
// All Rights Reserved.
//////////////////////////////////////////////////////////////////////

package com.stottlerhenke.simbionic.engine.core;
import java.util.*;
import com.stottlerhenke.simbionic.engine.SB_SingletonBook;
import com.stottlerhenke.simbionic.engine.file.SB_StreamReader;
import com.stottlerhenke.simbionic.engine.core.SB_Node;
import com.stottlerhenke.simbionic.api.*;
import com.stottlerhenke.simbionic.common.*;

/**
 * An instance of this class represents a single Behavior Transition
 * Network defined by the author.
 */
public class SB_Behavior extends SB_BehaviorElement 
{
  protected ArrayList	_nodes = new ArrayList();
  protected ArrayList	_conditions = new ArrayList();
  protected ArrayList	_transitions = new ArrayList();
  protected SB_Node	_initNode;
  protected ArrayList	_hierIndices;
  protected ArrayList	_hierDepths = new ArrayList();

  // one set of variables must be created for each instance of this behavior
  protected SB_VariableMap	_varTemplate;

  public SB_Behavior(SB_BehaviorElement owner) {
    super(owner);
    _initNode = null;
    _hierIndices = new ArrayList();//null;
  }
  /**
   * @return true if behavior has the named variable, false otherwise
   */
  public boolean HasVariable( String id){
    if (!_varTemplate.IsVariable(id))
                return true;
    return false;
  }

  /**
   * @return the name of this behavior
   */
  public String getName()  { return _owner.getName(); }

  /**
   * @return the initial node
   */
  public SB_Node GetInitNode() { return _initNode;}

  /**
   * @return the parent class of the behavior
   */
  public SB_BehaviorClass GetClass() { return (SB_BehaviorClass)_owner; }
  /**
   * @return the list of hierarchy depths (for polymorphism resolution)
   */
  public ArrayList GetHierarchyDepths() { return _hierDepths; }

  /**
   * @return the list of all nodes
   */
  public ArrayList GetNodes() {return _nodes;}

  /**
   * @return the node with the given id if found, null otherwise
   */
  public SB_Node GetNode(int id)
  {
      SB_Node returnValue = null;
      
    	int count = _nodes.size();
    	for( int x = 0; x < count; x++ )
    	{
    		SB_Node tempNode = (SB_Node) _nodes.get(x);
    		if(tempNode.getId() == id )
    		{
    		    returnValue = tempNode;
    		    break;
    		}
      }
      
      return returnValue;
  }
  
  /**
   * @return the list of all conditions
   */
  public ArrayList GetConditions() {return _conditions;}

  /**
   * @return the list of all transitions
   */
  public ArrayList GetTransitions() {return _transitions;}

  /**
   * @return the variable template for this behavior
   */
  public SB_VariableMap GetVarTemplate() { return _varTemplate; }

  /**
   * There can be at most one catch node per behavior
   * 
   * @return The catch node if one is specified for this node, null otherwise
   */
  public SB_Node GetCatchNode()
  {
  	int count = _nodes.size();
  	
  	for( int x = 0; x < count; x++ )
  	{
  		SB_Node tempNode = (SB_Node) _nodes.get(x);
  		if( tempNode.IsCatchNode())
  		{
  			return tempNode;
  		}
  	}
  	
  	return null;
  }
 
  /**
   * There can be at most one always node per behavior.
   * 
   * @return The always node if one is specified for this node, null otherwise
   */
  public SB_Node GetAlwaysNode()
  {
  	int count = _nodes.size();
  	
  	for( int x = 0; x < count; x++ )
  	{
  		SB_Node tempNode = (SB_Node) _nodes.get(x);
  		if( tempNode.IsAlwaysNode())
  		{
  			return tempNode;
  		}
  	}
  	
  	return null;
  }
  
	/**
	 * Loads a single polymorphism from a stream.
	 * @param reader the stream reader to load from
	 */
  public void load(SB_StreamReader reader,SB_SingletonBook book) 
		throws SB_FileException
	{
    if(SIM_Constants.DEBUG_INFO_ON)
      book.getLogger().log(".Loading polymorphic behavior...",SB_Logger.INIT);

    // read hierarchy indices 

    int numHiers = book.getBehaviorRegistry().getNumHierarchies();
    for (int hier=0; hier < numHiers; ++hier){
      String hierNode = reader.readPString();
      // store the index for debugging convenience
      _hierIndices.add(hier,hierNode);

      if(SIM_Constants.DEBUG_INFO_ON)
        book.getLogger().log(".Index: " + hierNode,SB_Logger.INIT);
         
      try {
      	_hierDepths.add(hier, new Integer(book.getBehaviorRegistry().indexBehavior(hier,hierNode,_owner.getName(),this)));
      } catch (SB_Exception e) {
      	throw new SB_FileException("Behavior " + getName() + " indexed by nonexistent descriptor " + hierNode);
      }
    }

    // read behavior variables 
    reader.readSectionTag(SB_StreamReader.TAG_VARIABLES_START);
    _varTemplate = new SB_VariableMap();
    try
    {
      _varTemplate.load(reader,book);
    }
    catch(SB_Exception ex)
    {
      throw new SB_FileException(ex.toString());
    }

    // read nodes 
    reader.readSectionTag(SB_StreamReader.TAG_NODES_START);
		int initNodeIndex = reader.readInt();
		
		int num = reader.readInt();
		
		if (initNodeIndex >= num)
		  throw new SB_FileException("Illegal initial node " + initNodeIndex + " in behavior spec.");
		
		// read nodes 
		SB_Node node;
		for (int index = 0; index < num; index++) 
		{
		  node = SB_Node.readNode(reader,book,this);
		  _nodes.add(node);
		}
		
		// assign the initial node
		_initNode = (SB_Node)_nodes.get(initNodeIndex);
		
		// read conditions 
		reader.readSectionTag(SB_StreamReader.TAG_CONDITIONS_START);
		num = reader.readInt();
		
		SB_Condition cond;
		for (int index = 0; index < num; index++) 
		{
		  cond = new SB_Condition(this);
		  cond.load(reader,book);
		  _conditions.add(cond);
		}
		
		// read transitions
		reader.readSectionTag(SB_StreamReader.TAG_TRANSITIONS_START);
		num = reader.readInt();
		
		// read in the transitions one by one
		SB_TransitionEdge trans;
		for (int i = 0; i < num; i++) 
		{
		  trans = new SB_TransitionEdge(this);
		  trans.load(reader,book);
		  _transitions.add(trans);
		}
  }

  public void initialize(SB_SingletonBook book)
		throws SB_Exception 
  {
    for (int i = 0; i < _nodes.size(); i++)
    {
    	((SB_Node)_nodes.get(i)).initialize(_transitions,book);
    }

    for (int j = 0; j < _conditions.size(); j++)
    {
    	((SB_Condition)_conditions.get(j)).initialize(_transitions,book);
    }
    
    for (int k = 0; k < _transitions.size(); k++)
    {
    	((SB_TransitionEdge)_transitions.get(k)).initialize(book);
    }
  }
  
  public void replaceMethod(SB_Method method)
  {
      if (method instanceof SB_Action)
      {
          // action -- check action nodes only
          SB_Action newAction = (SB_Action)method;
	      for (int i = 0; i < _nodes.size(); i++)
	      {
	          SB_Node node = (SB_Node)_nodes.get(i);
	          if (node instanceof SB_ActionNode)
	          {
	              ((SB_ActionNode)node).replaceAction(newAction);
	          }
	      }
      }
      else
      {
          // predicate -- check bindings and conditions
          SB_Function newFun = (SB_Function)method;
	      for (int i = 0; i < _nodes.size(); i++)
	      {
	          ((SB_Node)_nodes.get(i)).replaceFunction(newFun);
	      }
	      for (int j = 0; j < _conditions.size(); j++)
	      {
	      	((SB_Condition)_conditions.get(j)).replaceFunction(newFun);
	      }
	      
	      for (int k = 0; k < _transitions.size(); k++)
	      {
	      	((SB_TransitionEdge)_transitions.get(k)).replaceFunction(newFun);
	      }
      }
  }

  public ArrayList GetHierarchyIndices() {
    ArrayList strVec = new ArrayList();
    for (int i = 0; i < _hierIndices.size();  i++){
      strVec.add( _hierIndices.get(i) );
     }
     return strVec;
  }

  public String toString(){
    String out;
    out =  "Polymorph: " + getName() + " [";
    for (int i = 0; i < _hierIndices.size(); i++){
     out = out + ((String)_hierIndices.get(i)) + " ";
    }
    out = out + "]";
    return out;

  }

}